<!DOCTYPE html>
<html>
  <head>
    <title>HTML5 のオフライン機能</title>

    <meta charset='utf-8'>
  </head>
  
  <style>
    
    
  </style>

  <body style='display: none'>

  <section class='slides layout-regular template-default'>

  <article class="biglogo"></article>
  <article class="title-slide"> 
    <div> 
      <div data-config-logo></div> 
      <p data-config-name></p> 
      <h1>HTML5 のオフライン機能</h1> 
      <p class="info"> 
        <time datetime="YYYY-MM-DD" data-config-date>MM DD, 2011</time> &nbsp; - &nbsp; <span data-config-location></span> 
      </p> 
      <p class="info">
        <a href="http://goo.gl/FsKEq">http://goo.gl/FsKEq</a>
      </p>
      <!-- <img data-config-map class="rounded" title="Of course we're using Geolocation!" alt="Of course we're using Geolocation!">  -->
    </div> 
  </article>

  <article id="who">
    <h3>Who?</h3>
    <p>
      <img class="avatar rounded" data-config-pic>
    </p>
    <p>
      <a rel="author" data-config-gplus target="_blank">
        <img src="http://www.google.com/images/icons/ui/gprofile_button-44.png" width="44" height="44">
      </a> +<a rel="author" data-config-gplus target="_blank"><span data-config-name></span></a>
    </p>
    <br>
    <p>
      <a rel="author" data-config-twitter target="_blank" style="margin-left:-8px;">
        <img src="images/twitter_newbird_blue.png" width="58" height="58">
      </a> @ <a rel="author" data-config-twitter target="_blank" style="margin-left:-8px;"><span data-config-twitter></span></a>
    </p>
  </article>

  <article class="fill">
    <h2>デモ</h2>
  </article>

  <article>
    <h3>オフラインとは</h3>
    <ul>
      <li>インターネット回線が繋がっていなくても起動できる
      <li>インターネット回線が繋がっていなくても動く
      <li>インターネット回線が繋がっていなくてもデータが保存できる
      <li>オンラインに戻った際、クラウドのデータと矛盾が発生しない
  </article>

  <article>
    <h3>アジェンダ</h3>
    <ul>
      <li>動的データの保存
      <li>静的リソースの保存
      <li>バイナリデータの保存
      <li>データの同期
      <li>アプリのパフォーマンス向上
      <li>役立つ情報
  </article>

  <article class="fill"> 
    <h2>動的なデータの保存</h2>
  </article> 

  <article class="fill">
    <h3>動的なデータの保存</h3>
    <p>ユーザーデータや設定等</p>
    <h4>Web Storage (localStorage / sessionStorage)</h4>
    <ul>
      <li>構造化されていないデータの保存
      <li>Cookie に似た利用方法 (ただし HTTP リクエストには乗らない)
    </ul>
    <h4>IndexedDB / WebSQL</h4>
    <ul>
      <li>構造化されたデータの保存
      <li>トランズアクションを使い、同時に複数のデータ操作を行うことができる
    </ul>
  </article> 

  <article> 
    <h3>localStorage</h3>
    <ul>
    <li>シンプルなキーバリュー型保存領域
    <pre>
if (!localStorage.getItem("checkins")) {
  localStorage.setItem("checkins", JSON.stringify([]));
}</pre></li>

 <li>API も簡単
<pre>
setItem()
getItem()
removeItem()
clear()</pre></li>
  <li>文字列しか保存できない
  <li>同期型 API

  </article>
  <article>
    <h3>sessionStorage</h3> 
    <ul>
    <li>localStorage とほぼ同じ。ただし...
    <li>ブラウザを閉じると消されてしまう
    <li>新しいウィンドウやタブで開くと、別のセッションとして扱われる (Cookie のようには使えない)
    <li>一時的なデータに最適 (フォームの入力値保存等)
    </ul>
  </article>
  
  <article>
    <h3>Same Origin Policy</h3>
      <ul>
        <li>同じオリジンにスコープ</li>
      </ul>
      <pre>
  http://example.com:80/
    \       \         \_ port
     \       \_ domain
      \_ scheme
  </pre>
  </article>
  
  <article>
    <h3>IndexedDB とは</h3>
    <ul>
      <li>構造型データ (オブジェクト丸ごと) を保存することができる
      <li>スキーマがなく、SQL も使わない
      <li>インデックスを使って検索
      <li>現在は Chrome と Firefox のみが実装。ただし他のメジャーブラウザも対応予定。
      <li>非同期の API
    </ul>
  </article>
  <article>
    <h3>IndexedDB での検索</h3>

    <p>インデックスを取得 ( index ):</p>
  <pre>
// db.createObjectStore("Friend", "id", true);
db.<b>createIndex</b>("FriendNames", "name", false);
var index = db.<b>openIndex</b>('FriendNames');
var id = <b>index.get</b>('Eric');
</pre>
    <p>イテレート ( cursor ):</p>
    <pre>
// name の始まりを A-E に制限
var range = new <b>KeyRange.bound</b>('A', 'E');
var cursor = <b>index.openObjectCursor</b>(range);

while (<b>cursor.continue</b>()) {
  console.log(<b>cursor.value.name</b>);
}
</pre>
  </article>
  
  <article class="slide deck-after" id="indexeddb-example"> 
    <style>
      #indexeddb-example input { font-size: 16px; }
      #indexeddb-example .record-list li:nth-child(odd) { background-color: #eee; }
      #indexeddb-example .record-list li:nth-child(even) { background-color: #ddd; }
      #indexeddb-example .record-list li {
        padding-left: 5px;
      }
      #idb-results {
        max-height: 250px;
        overflow: auto;
        text-align: left;
        padding: 0;
      }
      .keyval {
        border: 1px dashed;
        padding: 4px;
        outline: none;
      }
      #indexeddb-example .error {
        color: red;
      }
    </style>
    <pre style="font-size: 16px">var idbRequest = window.<b>indexedDB</b>.<b>open</b>('Database Name');
idbRequest.onsuccess = function(event) {
  <b>var db = event.srcElement.result</b>;
  var transaction = db.<b>transaction</b>([], <b>IDBTransaction.READ_ONLY</b>);
  var curRequest = transaction.<b>objectStore</b>('ObjectStore Name').<b>openCursor</b>();
  curRequest.<b>onsuccess</b> = ...;
};</pre>
    <div class="center" id="indexeddb-example">
      <input type="text" placeholder="key" id="idb-key" size="10" /> <input type="text" placeholder="value" id="idb-value" size="15" />
      <button onclick="indexedDbSample.idbSet()">オブジェクトをセット</button>
      <button onclick="indexedDbSample.idbCreate()">Object Storeを作成</button>
      <button onclick="indexedDbSample.idbRemove()">Object Storeを削除</button>
      <div id="idb-log"></div>
      <div class="record-list" id="idb-results-wrapper"></div>
    </div>
    <script defer>
      var indexedDbSample = (function() {
        var idb_;
        var idbRequest_;
        var idbLog_ = document.getElementById('idb-log');
        var idResultsWrapper_ = document.getElementById('idb-results-wrapper');

        // IndexedDB spec is still evolving, various browsers keep it
        // behind various flags and implementation varies.
        if ('webkitIndexedDB' in window) {
          window.indexedDB = window.webkitIndexedDB;
          window.IDBTransaction = window.webkitIDBTransaction;
        } else if ('moz_indexedDB' in window) {
          window.indexedDB = window.moz_indexedDB;
        }

        // Open our IndexedDB if the browser supports it.
        if (window.indexedDB) {
          idbRequest_ = window.indexedDB.open("Test", "A test object store.");
          idbRequest_.onerror = idbError_;
          idbRequest_.addEventListener('success', function(event) {
            idb_ = event.srcElement.result;
            idbShow_(event);
          }, false);
        }

        function idbError_(event) {
          idbLog_.innerHTML += '<p class="error">Error: ' +
                               event.message + ' (' + event.code + ')</p>';
        }

        function idbShow_(event) {
          if (!idb_.objectStoreNames.contains('myObjectStore')) {
            idbLog_.innerHTML = "<p>Object Store はまだ作られていません</p>";
            return;
          }

          var html = [];
          var transaction = idb_.transaction([], IDBTransaction.READ_ONLY); // Read is default.
          var request = transaction.objectStore('myObjectStore').openCursor(); // Get all results.

          // This callback will continue to be called until we have no more results.
          request.onsuccess = function(e) {
            var cursor = e.srcElement.result;
            if (!cursor) {
              idResultsWrapper_.innerHTML = '<ul class="record-list" id="idb-results">' + html.join('') + '</ul>';
              return;
            }
            html.push('<li><span class="keyval" contenteditable onblur="indexedDbSample.updateKey(\'',
                      cursor.key, '\', this)">', cursor.key, '</span> ',
                      '=> <span class="keyval" contenteditable onblur="indexedDbSample.updateValue(\'',
                      cursor.key, '\', this)">', cursor.value, '</span>&nbsp; ',
                      '<a href="javascript:void(0)" onclick="indexedDbSample.deleteKey(\'',
                      cursor.key, '\')">[Delete]</a></li>');
            cursor.continue();
          }
          request.onerror = idbError_;
        }

        function idbCreate_() {
          if (!idb_) {
            if (idbRequest_) {
              idbRequest_.addEventListener('success', removeObjectStore, false); // If indexedDB is still opening, just queue this up.
            }
            return;
          }

          var request = idb_.setVersion('the new version string');
          request.onerror = idbError_;
          request.onsuccess = function(e) {
            if (!idb_.objectStoreNames.contains('myObjectStore')) {
              try {
                var objectStore = idb_.createObjectStore('myObjectStore', null); // FF is requiring the 2nd keyPath arg. It can be optional :(
                idbLog_.innerHTML = "<p>Object Store を作成しました</p>";
              } catch (err) {
                idbLog_.innerHTML = '<p class="error">' + err.toString() + '</p>';
              }
            } else {
              idbLog_.innerHTML = '<p class="error">Object store は既に存在します</p>';
            }
          }
        }

        function idbSet_() {
          if (!idb_) {
            if (idbRequest_) {
              idbRequest_.addEventListener('success', removeObjectStore, false); // If indexedDB is still opening, just queue this up.
            }
            return;
          }

          if (!idb_.objectStoreNames.contains('myObjectStore')) {
            idbLog_.innerHTML = "<p class=\"error\">Object Store が存在しません</p>";
            return;
          }

           // Create a transaction that locks the world.
          var objectStore = idb_.transaction([], IDBTransaction.READ_WRITE)
                                .objectStore("myObjectStore");
          var request = objectStore.put(
              document.getElementById('idb-value').value,
              document.getElementById('idb-key').value);
          request.onerror = idbError_;
          request.onsuccess = idbShow_;
        }

        function updateKey_(key, element) {
          var newKey = element.textContent;
          var transaction = idb_.transaction([], IDBTransaction.READ_WRITE); // Create a transaction that locks the world.
          var objectStore = transaction.objectStore("myObjectStore");
          var request = objectStore.get(key);
          request.onerror = idbError_;
          request.onsuccess = function(event) {
            var value = event.srcElement.result;
            if (objectStore.delete) {
              var request = objectStore.delete(key);
            } else {
              var request = objectStore.remove(key);
            }
            request.onerror = idbError_;
            request.onsuccess = function(event) {
              var request = objectStore.add(value, newKey);
              request.onerror = idbError_;
              request.onsuccess = idbShow_;
            };
          };
        }

        function updateValue_(key, element) {
          var transaction = idb_.transaction([], IDBTransaction.READ_WRITE); // Create a transaction that locks the world.
          var objectStore = transaction.objectStore("myObjectStore");
          var request = objectStore.put(element.textContent, key);
          request.onerror = idbError_;
          request.onsuccess = idbShow_;
        }

        function deleteKey_(key) {
          var transaction = idb_.transaction([], IDBTransaction.READ_WRITE); // Create a transaction that locks the world.
          var objectStore = transaction.objectStore("myObjectStore");
          if (objectStore.delete) {
            var request = objectStore.delete(key);
          } else {
            var request = objectStore.remove(key);
          }
          request.onerror = idbError_;
          request.onsuccess = idbShow_;
        }

        function idbRemove_() {
          if (!idb_) {
            if (idbRequest_) {
              idbRequest_.addEventListener('success', removeObjectStore, false); // If indexedDB is still opening, just queue this up.
            }
            return;
          }

          var request = idb_.setVersion("the new version string");
          request.onerror = idbError_;
          request.onsuccess = function(event) {

            if (idb_.objectStoreNames.contains('myObjectStore')) {
              try {
                // Spec has been updated to deleteObjectStore.
                if (idb_.deleteObjectStore) {
                  idb_.deleteObjectStore('myObjectStore');
                } else {
                  idb_.removeObjectStore('myObjectStore');
                }
                idResultsWrapper_.innerHTML = '';
                idbLog_.innerHTML = "<p>Object Store は破棄されました</p>";
              } catch (err) {
                idbLog_.innerHTML = '<p class="error">' + err.toString() + '</p>';
              }
            } else {
              idbLog_.innerHTML = "<p class=\"error\">Object Store が存在しません</p>";
            }
          };
        }

        return {
          idbSet: idbSet_,
          idbCreate: idbCreate_,
          idbRemove: idbRemove_,
          updateKey: updateKey_,
          updateValue: updateValue_,
          deleteKey: deleteKey_
        }
      })();
    </script>
    <ul>
      <li><a href="http://www.html5rocks.com/en/tutorials/indexeddb/todo/" target="_blank">A Simple TODO list using HTML5 IndexedDB</a></li>
    </ul>
  </article>

  <article>
    <h3>WebSQL DB</h3>
    <ul>
      <li>リレーショナルデータベース
      <li>非推奨 (今後この仕様が発展することはありません)
      <li>モバイル機器がターゲットの場合...?
      <li><a href="http://westcoastlogic.com/lawnchair/">Lawnchair</a>
    </ul>
  </article>
    
  <article class="fill"> 
    <h2>静的リソースの保存</h2>
  </article> 

  <article>
    <h3>静的リソースの保存</h3>
    <p>HTML や CSS, JS, 画像等</p>
    <h4>App Cache</h4>
    <ul>
      <li>ウェブアプリをまるごとローカルにキャッシュ！</li>
      <li>メリット
        <ul>
          <li>HTML や CSS、JavaScript を手軽にキャッシュできる</li>
          <li>リソースのキャッシュによってアプリはより高速に</li>
          <li>モバイルでのサポート率が高い
            <ul>
              <li>iPhone / Android の GMail アプリで実際に使われている</li>
            </ul></li>
        </ul>
      </li>
    </ul>
  </article>

  <article>
    <h3>App Cache</h3>
    <pre class="hcenter">
&lt;html <b>manifest="example.appcache"</b>&gt;... &lt;/html&gt;
</pre>
    <pre style="margin-top:10px;font-size:18px">
<b>CACHE MANIFEST</b>
# 2010-11-17-v0.0.1

# 明示的にキャッシュされるエントリ
<b>CACHE</b>:
index.html
stylesheet.css
images/logo.png

# ユーザーがオフラインの場合は static.html が表示される
<b>FALLBACK</b>:
/ /static.html

# ユーザーがオンラインの必要があるリソース
<b>NETWORK</b>:
*
</pre>
  </article>

  <article>
    <h3>App Cache を使いこなす</h3>
    <ul class=build>
      <li>App Cache を指定する HTML は無条件にキャッシュされる</li>
      <li>manifest の文法が間違っていると、キャッシュされない (<a href="http://manifest-validator.com/">validator</a> での確認がおすすめ)</li>
      <li>ひとつでも 404 だと、全てキャッシュされない</li>
    </ul>
  </article>

  <article>
    <h3>App Cache を使いこなす</h3>
    <ul class=build>
      <li>manifest ファイルの Content-Type は <code>text/cache-manifest</code></p>
      <li>manifest ファイルを一文字でも変更すれば自動的にバージョンアップ</li>
      <li>manifest ファイル生成ツール <a href="http://westciv.com/tools/manifestR/">manifestR</a></li>
      <li><a href="http://appcachefacts.info/">appcachefacts.info</a></li>
    </ul>
  </article>

  <!--article>
    <h3>キャシュのアップデートは 2 回目のリロードで提供される。これを回避するには...</h3>
    <pre style="font-size: 19px">
applicationCache.addEventListener('updateready', function(e){
  if (applicationCache.status == 
         applicationCache.UPDATEREADY){
    if (confirm('新しいコンテンツをロードしますか？')) {
      ...
    }
  }
});</pre>
  </article-->
  
  <article class="fill"> 
    <h2>バイナリデータの保存</h2>
  </article> 
  
  <article> 
    <h3>バイナリデータの保存</h3>
    <p>ユーザーデータのうちバイナリのもの</p>
    <h4>File System API</h4> 
    <ul>
      <li>ディレクトリを持ったファイルシステム上にデータをファイルとして保存できる
      <li>OS の持つファイルを操作できるわけではない
      <li>サンドボックス化されているため、他のオリジンからは切り離されている
      <li>画像や音声、動画などのバイナリデータ保存に最適
      <li>URL を使って直接参照することができる
      <li>非同期の API
    </ul>
  </article>

  <article> 
    <h3>ファイルシステムを開く</h3> 
    <pre>window.<b>requestFileSystem</b>(
  TEMPORARY,        // 永続的か、一時的か
  1024 * 1024,      // 必要な領域サイズ (バイト)
  initFs,           // 成功時のコールバック
  opt_errorHandler  // エラー時のコールバック (オプション)
);</pre>
  </article>

  <article>
    <h3>ファイルへの URL を作る 3 つの方法</h3> 
    <ul> 
      <li><span style="text-decoration: line-through;">Data URL を使う ( <code>data:</code> )</span></li> 
      <li><span style="text-decoration: line-through;">Blob URL を使う ( <code>blob:</code> )</span></li> 
      <li>File System の URL を使う  ( <code>filesystem:</code> ) <span class="new">New!</span></li> 
    </ul> 
    <pre>var img = document.createElement('img');

// filesystem:http://example.com/temporary/myfile.png
img.src = <b>fileEntry.toURL</b>();
document.body.appendChild(img);
</pre> 
  <p>Filesystem URL からファイルを取得する:</p> 
  <pre style="font-size: 16px"><b>window.resolveLocalFileSystemURL</b>(img.src, function(fileEntry) { ... });</pre>
  </article>

  <article> 
    <h3>サンプルコード: ドロップしたファイルを複製する</h3> 
    <pre style="font-size: 15px;">document.querySelector('#terminal').ondrop = function(e) {
  var files = <b>e.dataTransfer.files</b>;
  window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) {
    Array.prototype.slice.call(files || [], 0).forEach(function(file, i) {
      fs.root.getFile(file.name, {create: true, <b>exclusive: true</b>}, function(fileEntry) {
        <b>fileEntry.createWriter</b>(function(fileWriter) {
          <b>fileWriter.write(f)</b>; // write() は File | Blob を引数に取ることができる
        }, errorHandler);
      }, errorHandler);
    });
  }, errorHandler);
};</pre>
  <ul><li><a href="http://www.html5rocks.com/en/tutorials/file/filesystem/" target="_blank">Exploring the FileSystem APIs</a></li></ul>
  </article> 

  <article class="fill"> 
    <h3>DEMO: Peephole Extension</h3>
    <img src="images/HTML5_Offline_Storage_512.png" style="position:absolute; top:140px; left:220px; min-width:300px; min-height:300px;"/><br>
    <a href="http://playground.html5rocks.com/#filesystem_apis">Example page</a> 
    <p class="clickable"><a href="https://chrome.google.com/webstore/detail/nhnjmpbdkieehidddbaeajffijockaea" target="_blank">Install</a></p>
  </article>

  <article class="fill"> 
    <h2>データの同期</h2>
  </article> 

  <!--article> 
    <h3>データの同期</h3>
    <ul>
      <li>オフラインの間に蓄積したデータをどうやってサーバーと同期するのか
      <li>複数のオフライン環境からデータの整合性をどのように保つのか
    </ul>
  </article-->

  <article> 
    <h3>データの同期</h3>
    <ul class="build">
      <li>オンラインバージョンとオフラインバージョンは、起動時の処理を分ける
      <li>サーバーサイドの機能を補完するファイルを用意しておき、オフライン時に FALLBACK させる
      <li>テンプレートを使って、JavaScript で parse & fill
      <!--li>オフライン時に使えるように、XHR で呼び出された情報は保存しておく</li-->
    </ul>
  </article>

  <article> 
    <h3>データの同期: Shared Worker を使う方法</h3>
    <p>サーバーと直接通信する代わりに、オンライン時に通信を肩代わりし、変更点をローカルに保存してくれる Worker を用意し、これとやりとりをする。</p>
    <p>この Shared Worker により:</p>
    <ul>
      <li>接続状況を一元管理できる
      <li>同じアプリを別ウィンドウで開いていても、データの一貫性を保ってくれる
      <li>サーバー通信など、一部のコードを集中管理することで、複数ウィンドウにまたがったアプリのメモリ使用量を節約できる
    </ul>
  </article>
  <article> 
    <h3><code>navigator.onLine</code> - 通信状況の確認</h3> 
    <pre>if (<b>navigator.onLine</b>) {
  console.log('ONLINE!');
} else {
  console.log('Connection flaky');
}
</pre> 
<pre>window.addEventListener('online', function(e) {
  // サーバーとデータを再同期
}, false);

window.addEventListener('offline', function(e) {
  // サーバーとのやり取りをキューに追加
}, false);
</pre> 
  </article> 

  <article class="fill"> 
    <iframe src="http://html5-demos.appspot.com/static/navigator.onLine.html"></iframe> 
  </article>

  <article class="fill">
    <h2>アプリのパフォーマンス向上</h2>
  </article>

  <article>
    <h3>オフライン機能を使ってパフォーマンスを向上する</h3>
    <ul class="build">
      <li>Cookie の代わりに Web Storage を使う (Web Storage)
      <li>サーバーの代わりにクライアント側のデータベースを使う (IndexedDB / WebSQL)
      <li>オフラインアプリだけでなく、オンラインアプリでも App Cache を使う (App Cache)
      <li>変更・再利用するためバイナリファイルを保存しておく (FileSystem API)
    </ul>
  </article>

  <article>
    <h3>オフライン機能のデバッグ</h3>
    <img style="border: 2px solid grey; margin-top: 20px" src="images/devtools.png" />
    <ul>
      <li><a href="chrome://appcache-internals/">chrome://appcache-internals/</a>
      <li><a href="chrome://blob-internals/">chrome://blob-internals/</a>
      <li><a href="chrome://quota-internals/">chrome://quota-internals/</a>
    </ul>
  </article>

  <article class="fill">
    <h2>Quota</h2>
  </article>

  <article>
    <h3>Google Chrome でのクォータ</h3>
    <ul>
      <li>Temporary ストレージと Persistent ストレージ
      <li>Temporary ストレージ
        <ul>
          <li>Temporary ストレージのクォータは共有プール
          <li>プールのクォータは利用可能なディスク容量の 50%
          <li>各アプリが利用できるのはプールが持つクォータの 20%
          <li>プールのクォータを超過すると、最も古いアプリのローカル保存データが全て削除される
        </ul>
      <li>Persistent ストレージ
        <ul>
          <li>デフォルトクォータは 0MB
          <li>利用するためには Quota API にリクエストが必要
          <li>File System API にのみ適用される
        </ul>
    </ul>
  </article>

  <article>
    <h3>クォータ</h3>
    <table border="0" cellspacing="5" cellpadding="5">
      <tr><th></th><th>デフォルト (Temporary)</th><th>リクエストされた Quota (Persistent)</th></tr>
      <tr><td>Web Storage</td><td>5Mb</td><td rowspan="4">N/A</td></tr>
      <tr><td>App Cache</td><td rowspan="4">利用可能なディスク容量の 10%</td></tr>
      <tr><td>IndexedDB</td></tr>
      <tr><td>WebSQL</td></tr>
      <tr><td>File System API</td><td>任意</td></tr>
    </table>
  </article> 

  <article>
    <h3>Quota API</h3>
    <pre>
// 状況を問い合わせる
webkitStorageInfo.queryUsageAndQuota( 
    webkitStorageInfo.TEMPORARY,   // or PERSISTENT 
    usageCallback, 
    errorCallback); 

// クォータをリクエストする
webkitStorageInfo.requestQuota( 
    webkitStorageInfo.PERSISTENT
    newQuotaInBytes,
    quotaCallback, 
    errorCallback);</pre>
  </article>

  <article>
    <h3>Chrome ウェブアプリなら無限のクォータを得ることができます</h3>
    <p>manifest.json ファイルにパーミッション &quot;unlimitedStorage&quot; を追加することで、クォータを無制限にすることができます。</p>
    <p><a href="appmator.appspot.com">appmator.appspot.com</a> を利用して、簡単にアプリをパッケージ化できます。</p>
    <ul><li>
      <a href="http://code.google.com/chrome/extensions/manifest.html" target="_blank">Google Chrome Extensions - Google Code</a>
    </li></ul>

  </article>

  <article class="fill"> 
    <h2>役立つ情報</h2>
  </article> 

  <article>
    <h3>ブラウザサポート状況</h3>
    <table border="0" cellspacing="5" cellpadding="5">
      <tr>
        <th></th>
        <th><img src="http://paulirish.com/lovesyou/new-browser-logos/individual-browser-logos/64-firefox.png"></th>
        <th><img src="http://paulirish.com/lovesyou/new-browser-logos/individual-browser-logos/64-safari.png"></th>
        <th><img src="http://paulirish.com/lovesyou/new-browser-logos/individual-browser-logos/64-chrome.png"></th>
        <th><img src="http://paulirish.com/lovesyou/new-browser-logos/individual-browser-logos/Opera_128x128.png" width=64</th>
        <th><img src="http://paulirish.com/lovesyou/new-browser-logos/individual-browser-logos/64-ie.png"></th>
      </tr>
      <tr><td>Web Storage</td><td class="y">Y</td><td class="y">Y</td><td class="y">Y</td><td class="y">Y</td><td class="y">Y (8+)</td></tr>
      <tr><td>IndexedDB</td><td class="y">Y</td><td class="p">N</td><td class="y">Y</td><td class="p">N</td><td class="p">N</td></tr>
      <tr><td>WebSQL</td><td class="n">N</td><td class="y">Y</td><td class="y">Y</td><td class="y">Y</td><td class="n">N</td></tr>
      <tr><td>App Cache</td><td class="y">Y</td><td class="y">Y</td><td class="y">Y</td><td class="y">Y</td><td class="p">N</td></tr>
      <tr><td>File System API</td><td class="p">N</td><td class="p">N</td><td class="y">Y</td><td class="p">N</td><td class="p">N</td></tr>
    </table>
    <ul><li><a href="http://caniuse.com">caniuse.com</a></li></ul>
  </article> 

  <article>
    <h3>ブラウザサポート状況 (モバイル)</h3>
    <table border="0" cellspacing="5" cellpadding="5">
      
      <tr>
        <th></th>
        <th><img src="http://paulirish.com/lovesyou/new-browser-logos/individual-browser-logos/64-firefox.png"></th>
        <th><img src="http://paulirish.com/lovesyou/new-browser-logos/individual-browser-logos/64-safari.png"></th>
        <th><img src="http://gigapple.files.wordpress.com/2009/09/android_logo.png?w=64"></th>
        <th><img src="http://paulirish.com/lovesyou/new-browser-logos/individual-browser-logos/Opera_128x128.png" width=64</th>
        <th><img src="http://www.phonemag.com/blog/wp-content/uploads/2008/02/windows_mobile_logo.jpg" width=64></th>
      </tr>

      <tr><td>Web Storage</td><td class="y">Y</td><td class="y">Y</td><td class="y">Y (2+)</td><td class="y">Y</td><td class="y">Y</td></tr>
      <tr><td>IndexedDB</td><td class="y">Y</td><td class="p">N</td><td class="p">N</td><td class="p">N</td><td class="p">N</td></tr>
      <tr><td>WebSQL</td><td class="n">N</td><td class="y">Y</td><td class="y">Y (2+)</td><td class="y">Y</td><td class="n">N</td></tr>
      <tr><td>App Cache</td><td class="y">Y</td><td class="y">Y</td><td class="y">Y (2.1+)</td><td class="y">Y</td><td class="p">N</td></tr>
      <tr><td>File System API</td><td class="p">N</td><td class="p">N</td><td class="y">Y (3+)</td><td class="p">N</td><td class="p">N</td></tr>
    </table>
    <ul><li><a href="http://mobilehtml5.org/">mobilehtml5.org</a></li></ul>
  </article> 
  
  <article>
    <h3>Polyfill</h3>
    <p>Web Storage (LocalStorage と SessionStorage)</p>
    <ul>
      <li><a href="http://gist.github.com/350433">storage polyfill</a> by remy sharp
      <li><a href="http://code.google.com/p/sessionstorage/">sessionstorage</a> by andrea giammarchi
      <li><a href="http://amplifyjs.com/">Amplify.js</a> by appendTo HTML5 API with fallbacks for HTML4 browsers (including IE6)
      <li><a href="https://github.com/marcuswestin/store.js">store.js</a> - with fallbacks for legacy browsers
      <li><a href="http://developer.yahoo.com/yui/3/cache/#offline">YUI3 CacheOffline</a> by YUI team
    </ul>
  </article>

  <article>
    <h3>More Resources</h3>
    <ul>
      <li><a href="http://rezitech.github.com/stash/">Stash</a> - JavaScript オフラインストレージライブラリ
      <li><a href="http://westcoastlogic.com/lawnchair/">Lawnchair</a> - シンプルな json storage
      <li><a href="http://smus.com/game-asset-loader">Game Asset Loader</a> - App Cache の欠点を解決
    </ul>
    <a href="http://www.amazon.com/Using-HTML5-Filesystem-Eric-Bidelman/dp/1449309453"><img style="border: 2px solid grey" src="images/book.jpg" /></a>
  </article>
  
  <article>
    <h1>Thank you!</h1>

    <p>Questions?

  </article>
  
  </section>
  <script src="js/config-jp.js"></script>
  <script src='js/slides.js'></script>

  </body>
</html>
